﻿using Newtonsoft.Json;          // 提供 JSON 反序列化 (需要引用 Newtonsoft.Json 库)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net.Http;          // 提供 HttpClient
using System.Text;
using System.Text.RegularExpressions; // 用于正则表达式
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading; // 引入 CancellationTokenSource 命名空间
using System.Configuration; // 引入 ConfigurationManager 命名空间


namespace ShangWeiDemo1
{
    public partial class Main : Form
    {
        // 1. 串口对象：用于建立和管理物理连接
        private SerialPort _serialPort;

        // 2. 扫码数据缓存：用于处理串口数据碎片化（防止一次收不全）
        private StringBuilder _serialBuffer = new StringBuilder();

        // 3. HttpClient实例，用于发送HTTP请求
        private HttpClient _httpClient;

        // 用于日志文件操作的线程同步锁对象
        private static readonly object _logFileLock = new object();

        // ==========================================================
        // 🚀 核心新增：检测流程控制字段
        // ==========================================================

        /// <summary>
        /// 标记 MES/VisionMaster 接口调用是否正在进行中（检测周期锁）。
        /// 只要检测未完成，就锁定扫码。
        /// </summary>
        private bool _isVisionMasterQueryInProgress = false;

        /// <summary>
        /// 记录当前正在进行视觉检测的产品条码，用于识别重扫和结果匹配。
        /// </summary>
        private string _currentProductBarcode = string.Empty;

        // ==========================================================

        // 4. API接口响应数据模型 (嵌套类)
        private class ApiResponse
        {
            public int code { get; set; }
            public ApiData data { get; set; }
            public string msg { get; set; }
            public bool success { get; set; }
        }

        // 5. API响应数据中的详细数据模型 (嵌套类)
        private class ApiData
        {
            public bool isComplete { get; set; }
            public string reason { get; set; }
            public string specs { get; set; }
            public string u9No { get; set; }
        }

        // 6. TCP 客户端对象 (用于发送 MES 结果到 VisionMaster)
        private TcpClient _tcpClient;

        // 7. TCP Server 配置 (用于发送 MES 结果到 VisionMaster)
        private const string TcpServerIp = "192.168.30.83";
        private const int TcpServerPort = 9527;

        // 用于线程同步，确保连接操作不会冲突
        private static readonly object _tcpLock = new object();

        // ==========================================================
        // NEW: VisionMaster 结果监听配置 (端口 9528)
        // ==========================================================
        private const int ResultListenPort = 9528;
        private TcpListener _resultListener;
        private CancellationTokenSource _cts = new CancellationTokenSource();
        // ==========================================================


        public Main()
        {
            InitializeComponent();
            this.FormClosing += Main_FormClosing;

            // 初始化 HttpClient
            _httpClient = new HttpClient();
            _httpClient.Timeout = TimeSpan.FromSeconds(10);

            // 启动时立即尝试根据保存的配置连接串口
            InitializeSerialConnection();

            // NEW: 启动 VisionMaster 结果监听服务
            StartVisionResultListener();
        }

        // NEW: 窗体关闭事件，用于停止所有后台任务和释放资源
        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            // 1. 停止 TCP 结果监听线程
            _cts?.Cancel();
            try { _resultListener?.Stop(); } catch { }

            // 2. 关闭串口
            if (_serialPort != null && _serialPort.IsOpen)
            {
                _serialPort.Close();
                _serialPort.Dispose();
            }

            // 3. 关闭 TCP 客户端连接
            lock (_tcpLock)
            {
                if (_tcpClient != null)
                {
                    _tcpClient.Close();
                    _tcpClient = null;
                }
            }
        }


        // ==========================================================
        // NEW: VisionMaster 结果监听逻辑 (端口 9528) - 保持不变
        // ==========================================================

        /// <summary>
        /// 启动 VisionMaster 结果监听服务 (Server)
        /// </summary>
        private async void StartVisionResultListener()
        {
            try
            {
                // 监听所有网络接口的 IP 地址
                _resultListener = new TcpListener(System.Net.IPAddress.Any, ResultListenPort);
                _resultListener.Start();
                WriteToLogFile($"VisionMaster 结果监听服务已启动，端口: {ResultListenPort}");

                // 异步循环接收客户端连接
                while (!_cts.Token.IsCancellationRequested)
                {
                    // 使用 _cts.Token 优雅地处理关闭时的阻塞
                    Task<TcpClient> acceptTask = _resultListener.AcceptTcpClientAsync();

                    // 等待接受连接或取消信号
                    await Task.WhenAny(acceptTask, Task.Delay(Timeout.Infinite, _cts.Token));

                    if (_cts.Token.IsCancellationRequested)
                    {
                        break; // 收到取消信号，退出循环
                    }

                    // 接受客户端连接
                    TcpClient client = await acceptTask;
                    // 异步处理该客户端数据，不阻塞主循环
                    _ = HandleVisionMasterResultClient(client);
                }
            }
            catch (ObjectDisposedException) when (_cts.IsCancellationRequested)
            {
                // 正常关闭，忽略
            }
            catch (Exception ex)
            {
                if (!_cts.IsCancellationRequested)
                {
                    WriteToLogFile($"VisionMaster 结果监听失败: {ex.Message}");
                }
            }
        }

        /// <summary>
        /// 处理 VisionMaster 发送的 OK/NG 结果数据
        /// </summary>
        private async Task HandleVisionMasterResultClient(TcpClient client)
        {
            WriteToLogFile($"VisionMaster 连接接入: {client.Client.RemoteEndPoint}");

            using (NetworkStream stream = client.GetStream())
            using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
            {
                try
                {
                    // 长连接循环：持续读取数据，直到连接断开或程序取消
                    while (!_cts.Token.IsCancellationRequested && client.Connected)
                    {
                        // 异步读取一行数据
                        string result = await reader.ReadLineAsync();

                        // 检查客户端是否主动断开连接
                        if (string.IsNullOrEmpty(result))
                        {
                            WriteToLogFile("VisionMaster 客户端主动关闭连接。");
                            break;
                        }

                        // 处理接收到的数据
                        WriteToLogFile($"接收到 VisionMaster 原始结果: [{result.Trim()}]");

                        string[] parts = result.Trim().Split(',');
                        if (parts.Length == 2)
                        {
                            string barcode = parts[0].Trim();
                            string finalResult = parts[1].Trim().ToUpper(); // 转换为大写确保统一比较

                            // 核心：调用 ProcessFinalResult 更新 UI (必须通过 BeginInvoke)
                            this.BeginInvoke(new Action(() => ProcessFinalResult(barcode, finalResult)));
                        }
                        else
                        {
                            WriteToLogFile($"VisionMaster 结果格式错误: [{result.Trim()}]");
                        }
                    }
                }
                catch (IOException ioEx)
                {
                    WriteToLogFile($"VisionMaster 客户端 I/O 错误 (连接断开): {ioEx.Message}");
                }
                catch (Exception ex)
                {
                    WriteToLogFile($"处理 VisionMaster 结果时发生异常: {ex.Message}");
                }
                finally
                {
                    // 确保 TcpClient 资源被释放和连接被关闭
                    if (client.Connected)
                    {
                        client.Close();
                    }
                    client.Dispose();
                }
            }
            WriteToLogFile($"VisionMaster 连接断开。");
        }

        /// <summary>
        /// 处理最终的检测结果 (OK/NG)，更新 UI 状态灯 (在大正方形 Label 上)
        /// </summary>
        private void ProcessFinalResult(string barcode, string finalResult)
        {
            // 此方法已通过 BeginInvoke 切换到 UI 线程，可以直接操作控件
            WriteToLogFile($"最终检测结果 - 条码: {barcode}, 结果: {finalResult}");

            // ==========================================================
            // 🚀 核心新增：原子性检测周期结束控制
            // ==========================================================

            // 1. 检查条码是否与当前正在检测的产品匹配
            if (!barcode.Equals(_currentProductBarcode, StringComparison.OrdinalIgnoreCase))
            {
                // 收到非当前产品的结果（可能是上一个产品的重复结果）
                WriteToLogFile($"【结果忽略】收到条码 [{barcode}] 结果，但当前检测的产品为 [{_currentProductBarcode}]，忽略本次结果。");
                return;
            }

            // 2. 收到当前产品的第一个有效结果，检测流程结束，释放锁和标识
            if (_isVisionMasterQueryInProgress)
            {
                _isVisionMasterQueryInProgress = false;
                _currentProductBarcode = string.Empty;
                WriteToLogFile("【扫码锁】VisionMaster 结果返回，释放扫码锁。");
            }
            else
            {
                // 收到重复结果，流程已释放，直接处理 UI/Log，不改变锁状态
                WriteToLogFile($"【重复结果】收到条码 [{barcode}] 的重复结果，锁已释放。");
            }

            // ==========================================================
            // END 核心新增
            // ==========================================================

            // 确保 statusTextLabel 控件存在
            if (this.statusTextLabel == null)
            {
                WriteToLogFile("错误: statusTextLabel 控件未找到或未初始化!");
                return;
            }

            // 3. 更新大正方形 Label (statusTextLabel)
            if (finalResult == "OK")
            {
                this.statusTextLabel.Text = "OK";
                // 鲜明的绿色
                this.statusTextLabel.BackColor = System.Drawing.Color.LimeGreen;
                // 白色字体在绿色背景上很醒目
                this.statusTextLabel.ForeColor = System.Drawing.Color.White;
            }
            else if (finalResult == "NG")
            {
                this.statusTextLabel.Text = "NG";
                // 红色
                this.statusTextLabel.BackColor = System.Drawing.Color.Red;
                this.statusTextLabel.ForeColor = System.Drawing.Color.White;
            }
            else
            {
                this.statusTextLabel.Text = "N/A";
                this.statusTextLabel.BackColor = System.Drawing.Color.Yellow;
                this.statusTextLabel.ForeColor = System.Drawing.Color.Black;
            }

            // 4. TODO: 在此处添加后续逻辑（例如：如果OK，调用贴标签/数据汇报）
        }

        // ==========================================================
        // END OF NEW CODE
        // ==========================================================

        private void 扫码枪串口ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // 实例化 Form_Config 窗体
            Form_Config configForm = new Form_Config();

            // 使用 ShowDialog() 以模态方式显示配置窗口
            DialogResult result = configForm.ShowDialog();

            // 如果用户点击了“确认”
            if (result == DialogResult.OK)
            {
                // 1. 尝试初始化串口连接（InitializeSerialConnection 内部会处理成功或失败的消息）
                InitializeSerialConnection();

            }
        }

        // 初始化串口连接 - 保持不变
        private void InitializeSerialConnection()
        {
            // 1. 清理：如果旧的串口对象存在且处于打开状态，先关闭和销毁
            if (_serialPort != null && _serialPort.IsOpen)
            {
                _serialPort.Close();
                _serialPort.Dispose();
                _serialPort = null;
            }

            // 2. 从应用设置中获取配置的端口名
            string portName = Properties.Settings.Default.PortName;

            if (string.IsNullOrEmpty(portName))
            {
                // 如果未配置端口，则不进行连接
                return;
            }

            try
            {
                // 3. 实例化并设置参数
                _serialPort = new SerialPort(portName);

                // 默认使用常用的扫码枪通信参数
                _serialPort.BaudRate = 9600;
                _serialPort.DataBits = 8;
                _serialPort.Parity = Parity.None;
                _serialPort.StopBits = StopBits.One;

                // 4. 绑定核心事件：数据接收事件
                _serialPort.DataReceived += SerialPort_DataReceived;

                // 5. 打开端口
                _serialPort.Open();

                // 可以在这里添加代码更新状态栏显示连接成功

            }
            catch (Exception ex)
            {
                // 6. 连接失败处理
                if (_serialPort != null)
                {
                    _serialPort.Dispose();
                    _serialPort = null;
                }
                // 提示用户连接失败的原因
                MessageBox.Show($"串口连接失败 ({portName}): {ex.Message}，请检查设备或配置。", "连接错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 实现串口数据接收和缓存逻辑 - 保持不变
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (_serialPort == null || !_serialPort.IsOpen) return;

            try
            {
                // 1. 读取缓冲区所有可用数据
                string receivedData = _serialPort.ReadExisting();
                _serialBuffer.Append(receivedData);

                // 2. 检查缓存中是否包含扫码枪的终止符（回车 \r 或换行 \n）
                char[] terminators = { '\r', '\n' };
                int endOfCodeIndex = _serialBuffer.ToString().IndexOfAny(terminators);

                // 循环处理所有完整的扫码内容
                while (endOfCodeIndex > -1)
                {
                    // 提取完整的启动码 (Trim() 移除前后空白符，包括终止符本身)
                    string completeCode = _serialBuffer.ToString().Substring(0, endOfCodeIndex).Trim();

                    // 移除已处理的代码和终止符 (注意：+1 确保移除终止符)
                    _serialBuffer.Remove(0, endOfCodeIndex + 1);

                    if (!string.IsNullOrEmpty(completeCode))
                    {
                        // 核心操作：线程安全地更新 UI
                        ProcessScannedDataSafe(completeCode);
                    }

                    // 检查缓存中是否还有下一个完整的代码
                    endOfCodeIndex = _serialBuffer.ToString().IndexOfAny(terminators);
                }
            }
            catch (Exception)
            {
                // 记录异常，防止辅助线程崩溃
            }
        }

        // 增加 ShowMessage 辅助方法
        private void ShowMessage(string message)
        {
            // 使用 BeginInvoke 确保在 UI 线程上显示消息和更新 UI
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action<string>(ShowMessage), message);
                return;
            }

            // 假设您有一个 Label 或 TextBox 用于显示即时提示
            // 临时将信息显示在 startCode_textBox
            this.startCode_textBox.BackColor = System.Drawing.Color.Yellow;
            this.startCode_textBox.Text = message;
            Application.DoEvents();
        }

        // 实现线程安全地处理和验证扫码数据 - 核心修改
        /// <summary>
        /// 实现线程安全地处理和验证扫码数据
        /// </summary>
        private void ProcessScannedDataSafe(string barcode)
        {
            // 检查当前线程是否为 UI 线程
            if (this.InvokeRequired)
            {
                // 如果不是，将更新操作封送回 UI 线程
                this.BeginInvoke(new Action<string>(ProcessScannedDataSafe), barcode);
                return;
            }

            // --- 以下代码在 UI 主线程执行 ---

            // 1. **检测锁状态，并处理重扫/新件冲突**
            if (_isVisionMasterQueryInProgress)
            {
                // 正在等待 VisionMaster 结果
                if (barcode.Equals(_currentProductBarcode, StringComparison.OrdinalIgnoreCase))
                {
                    // 案例 1：等待结果时，重新扫入的是同一件产品 -> 允许重检逻辑，但提示操作员
                    ShowMessage("⚠️ 重复扫码：该产品检测结果尚未返回，等待中...");
                    WriteToLogFile($"【重检忽略】当前产品 [{barcode}] 结果等待中，忽略重复扫码。");
                    return; // 忽略本次扫码，等待结果
                }
                else
                {
                    // 案例 2：等待结果时，扫入的是另外一件产品 -> 拒绝新产品
                    ShowMessage("🚫 等待前件检测结果返回中，请稍后再扫码！");
                    WriteToLogFile($"【拒绝新件】前件 [{_currentProductBarcode}] 结果等待中，拒绝新件 [{barcode}]。");
                    return; // 拒绝新件
                }
            }

            // === 非锁定状态：新的检测流程开始 ===

            this.startCode_textBox.Text = barcode;
            string scanResult = barcode;

            // 定义启动码的正则表达式模式
            string pattern = @"^[0-9]{2,3}\-[a-zA-Z]*MO[0-9]*\$\$[0-9]*";
            string logMsg = string.Empty;

            try
            {
                if (Regex.IsMatch(scanResult, pattern, RegexOptions.IgnoreCase))
                {
                    // === 合法分支：匹配成功 ===

                    // 2. 🚀 设置状态锁和产品标识 (原子操作的开始)
                    _isVisionMasterQueryInProgress = true;
                    _currentProductBarcode = scanResult;   // 记录当前启动的产品码

                    // 强制显示绿色
                    this.startCode_textBox.BackColor = System.Drawing.Color.LightGreen;
                    this.startCode_textBox.Text = scanResult;

                    Application.DoEvents();

                    logMsg = "扫码输入启动码：匹配成功：" + scanResult;
                    WriteToLogFile("--------------------------------------------------");
                    WriteToLogFile(logMsg);

                    // 3. 触发 MES 接口查询 (合法时才调用)
                    TriggerMesQuery(scanResult);
                }
                else
                {
                    // === 非法分支：未匹配成功 ===

                    // 清除锁和产品标识 (保险措施，尽管此时应为 false)
                    _isVisionMasterQueryInProgress = false;
                    _currentProductBarcode = string.Empty;

                    // 强制显示红色
                    this.startCode_textBox.BackColor = System.Drawing.Color.Red;
                    this.startCode_textBox.Text = $"非法格式: {scanResult}";

                    Application.DoEvents();

                    logMsg = "扫码输入非法字符：未匹配：" + scanResult;
                    WriteToLogFile(logMsg);
                }
            }
            catch (Exception ex)
            {
                // 异常捕获
                _isVisionMasterQueryInProgress = false;
                _currentProductBarcode = string.Empty;
                this.startCode_textBox.BackColor = System.Drawing.Color.Yellow;
                this.startCode_textBox.Text = $"内部错误: {ex.Message}";
                WriteToLogFile($"【异常】处理失败: {ex.Message}");
            }
        }


        // 线程安全地写入日志 - 保持不变
        private void WriteToLogFile(string msg)
        {
            // === 核心修改：在写入 UI 之前进行格式化 ===
            string formattedMsg = msg;

            // 检查日志是否包含原始 JSON 标记，只对它进行格式化
            if (msg.StartsWith("MES 接口返回原始 JSON:"))
            {
                // 提取 JSON 部分
                int jsonStartIndex = msg.IndexOf(":") + 1;
                if (jsonStartIndex < msg.Length)
                {
                    string rawJson = msg.Substring(jsonStartIndex).Trim();
                    string prettyJson = FormatJson(rawJson); // 调用格式化方法

                    // 重新组合消息：标记 + 换行 + 格式化后的 JSON
                    formattedMsg = msg.Substring(0, jsonStartIndex).Trim() + Environment.NewLine + prettyJson;
                }
            }

            // 将格式化后的消息发送到 UI 界面
            AppendLogToUI(formattedMsg);

            // 使用 lock 关键字确保只有一个线程能进入文件写入操作
            lock (_logFileLock)
            {
                // 文件日志部分保持不变 (文件日志内容仍使用原始 msg 记录，或使用 formattedMsg)
                try
                {
                    // ... (省略路径和文件名逻辑) ...
                    string logDir = Path.Combine(@"D:\", "ShangWeiDemoLogs");
                    // 确保目录存在
                    if (!Directory.Exists(logDir))
                    {
                        Directory.CreateDirectory(logDir);
                    }
                    // ... (确保目录存在) ...
                    string logFileName = DateTime.Now.ToString("yyyyMMdd") + "_ScanLog.txt";
                    string logPath = Path.Combine(logDir, logFileName);

                    // 3. 格式化日志信息：时间戳 + 消息内容
                    // 💡 建议：文件日志仍然记录完整的 JSON，使用 formattedMsg
                    string logLine = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} - {formattedMsg}";

                    // 4. 使用 File.AppendAllText 安全地追加到文件末尾
                    File.AppendAllText(logPath, logLine + Environment.NewLine);
                }
                catch (Exception)
                {
                    // 忽略文件写入失败
                }
            }
        }

        // Main.cs (在 Main 类内部) - 保持不变
        /// <summary>
        /// 异步检查前工序状态的HTTP GET请求
        /// </summary>
        private async Task<string> CheckPreviousProcessAsync(string barcode)
        {
            string workStation = "5"; // 示例值，请替换为您的实际获取逻辑

            string baseUrl = "https://mes.jsxq.group/mes/api/v1/checkLines/checkLineResultByCockpitBom/{0}";
            string url;

            if (!string.IsNullOrEmpty(workStation))
            {
                // 假设您的 MES 接口要求 workStation 放在 URL Query 中
                url = string.Format(baseUrl + "?workStation={1}", barcode, workStation);
            }
            else
            {
                url = string.Format(baseUrl, barcode);
            }

            // 使用 _httpClient
            HttpResponseMessage response = await _httpClient.GetAsync(url);

            // 这行会在 HTTP 状态码非 2xx 时抛出异常
            response.EnsureSuccessStatusCode();

            return await response.Content.ReadAsStringAsync();
        }


        /// <summary>
        /// 触发 MES 接口查询并处理结果，更新 UI
        /// </summary>
        /// <param name="barcode">已校验通过的条码</param>
        private async void TriggerMesQuery(string barcode)
        {
            // 在异步操作开始前，清空旧的 MES 结果并设置默认状态
            this.u9No_textBox.Text = "";
            this.partNo_textBox.Text = "";
            this.isFinish_textBox.Text = "";
            this.isFinish_textBox.BackColor = System.Drawing.Color.White;

            string resultJson = string.Empty; // 用于存储原始 JSON 结果

            // 关键新增：定义状态变量，默认为失败/未完成
            bool isCompleteStatus = false;

            try
            {
                // 1. 异步调用接口
                resultJson = await CheckPreviousProcessAsync(barcode);

                // 核心实现：将原始 JSON 字符串写入日志
                WriteToLogFile($"MES 接口返回原始 JSON: {resultJson}");

                // 2. JSON 反序列化
                ApiResponse response = JsonConvert.DeserializeObject<ApiResponse>(resultJson);

                // 3. 结果判断与 UI 更新 (只在成功返回时更新)
                if (response != null && response.success && response.code == 200 && response.data != null)
                {
                    // 提取数据
                    string u9No = response.data.u9No ?? "N/A";
                    string specs = response.data.specs ?? "N/A";
                    bool isComplete = response.data.isComplete;

                    // 核心修改：更新状态变量
                    isCompleteStatus = isComplete;

                    string completeText;
                    System.Drawing.Color completeColor;

                    // 3a. 填充 UI 字段 (U9号 和 零件号)
                    this.u9No_textBox.Text = u9No;
                    this.partNo_textBox.Text = specs;

                    // 3b. 处理 isComplete 状态并设置颜色
                    if (isComplete)
                    {
                        completeText = "是";
                        completeColor = System.Drawing.Color.LightGreen;
                    }
                    else
                    {
                        completeText = "否";
                        completeColor = System.Drawing.Color.Red;
                    }

                    // 3c. 填充 UI 字段 (前工序完成情况)
                    this.isFinish_textBox.Text = completeText;
                    this.isFinish_textBox.BackColor = completeColor;

                    // 3d. 记录日志
                    string logResultMsg = isComplete
                         ? $"前工序检查通过: 前工序已完成，产品编码: {specs}"
                         : $"前工序检查未通过，原因: {response.data.reason ?? "未知原因"}";

                    WriteToLogFile(logResultMsg);
                }
                else
                {
                    // 接口返回非 200/success=false 或 data 为 null 的异常状态
                    string errorMsg = response != null ? response.msg : "未知错误";
                    string logErrorMsg = $"MES 接口返回异常: {errorMsg}";

                    // 失败时，设置前工序框为红色，显示错误信息
                    this.isFinish_textBox.Text = "接口异常";
                    this.isFinish_textBox.BackColor = System.Drawing.Color.Red;
                    this.u9No_textBox.Text = "";
                    this.partNo_textBox.Text = "";

                    WriteToLogFile(logErrorMsg);
                }

                // 4. TCP 发送核心逻辑 (在 try/catch 块之后执行，确保每次都发送)
                // 构造要发送的数据：启动码,完成状态(true/false)
                string dataToSend = $"{barcode},{isCompleteStatus.ToString().ToLower()}";

                // 异步发送数据 (使用 Task.Run 避免阻塞 UI 线程)
                await Task.Run(() => SendDataToTcpServer(dataToSend));

            }
            catch (HttpRequestException httpEx)
            {
                // 网络/HTTP 错误 (如 404, 500)
                string errorMsg = $"网络/接口调用失败: {httpEx.Message}";
                this.isFinish_textBox.Text = "调用失败";
                this.isFinish_textBox.BackColor = System.Drawing.Color.Red;
                WriteToLogFile(errorMsg);

                // 🚀 核心修改：MES/网络/解析失败，立即释放锁和产品标识
                if (_isVisionMasterQueryInProgress)
                {
                    _isVisionMasterQueryInProgress = false;
                    _currentProductBarcode = string.Empty; // 释放产品标识
                    WriteToLogFile("【注意】MES 调用或解析失败，立即释放扫码锁。");
                }
            }
            catch (Exception ex)
            {
                // JSON 解析或其他内部处理错误
                string errorMsg = $"内部处理错误: {ex.Message} (原始返回: {resultJson})";
                this.isFinish_textBox.Text = "处理错误";
                this.isFinish_textBox.BackColor = System.Drawing.Color.Red;
                WriteToLogFile(errorMsg);

                // 🚀 核心修改：MES/网络/解析失败，立即释放锁和产品标识
                if (_isVisionMasterQueryInProgress)
                {
                    _isVisionMasterQueryInProgress = false;
                    _currentProductBarcode = string.Empty; // 释放产品标识
                    WriteToLogFile("【注意】MES 调用或解析失败，立即释放扫码锁。");
                }
            }
            // 成功路径下，锁的释放由 ProcessFinalResult 负责
        }


        // 以下函数保持不变...

        /// <summary>
        /// 线程安全地将日志内容追加到 UI 文本框
        /// </summary>
        private void AppendLogToUI(string message)
        {
            // 确保控件已创建且不为 null
            if (this.logOutput_textBox == null) return;

            // 检查当前线程是否为 UI 线程
            if (this.logOutput_textBox.InvokeRequired)
            {
                // 封送回 UI 线程执行
                this.BeginInvoke(new Action<string>(AppendLogToUI), message);
                return;
            }

            // --- 以下代码在 UI 主线程执行 ---

            string logLine = $"{DateTime.Now:HH:mm:ss.fff} - {message}{Environment.NewLine}";

            // 限制行数，防止内存占用过大（可选，但推荐）
            if (this.logOutput_textBox.Lines.Length > 1000)
            {
                // 移除最旧的 100 行
                List<string> lines = this.logOutput_textBox.Lines.ToList();
                lines.RemoveRange(0, 100);
                this.logOutput_textBox.Lines = lines.ToArray();
            }

            // 追加新行并自动滚动到底部
            this.logOutput_textBox.AppendText(logLine);
        }

        private void viewLogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem menuItem = (ToolStripMenuItem)sender;

            // 确保控件已创建且不为 null
            if (this.logOutput_textBox != null)
            {
                // 直接使用菜单项的 Checked 状态来设置文本框的可见性
                this.logGroupBox.Visible = menuItem.Checked;
            }
        }

        /// <summary>
        /// 尝试对给定的 JSON 字符串进行格式化（Pretty Print）。
        /// 如果失败，返回原始字符串。
        /// </summary>
        private string FormatJson(string jsonString)
        {
            try
            {
                // 1. 解析 JSON 字符串为一个 JObject 或 JArray (Json.NET)
                var parsedJson = JsonConvert.DeserializeObject(jsonString);

                // 2. 将解析后的对象以 Indented（缩进）格式序列化回字符串
                return JsonConvert.SerializeObject(parsedJson, Formatting.Indented);
            }
            catch
            {
                // 解析失败，说明它不是有效的 JSON，直接返回原始字符串
                return jsonString;
            }
        }

        /// <summary>
        /// 确保 TCP 连接是打开的。如果未连接，则尝试连接。
        /// </summary>
        private bool EnsureTcpConnection()
        {
            lock (_tcpLock)
            {
                // 1. 如果 client 为空或未连接，尝试重新连接
                if (_tcpClient == null || !_tcpClient.Connected)
                {
                    // 先清理旧对象
                    if (_tcpClient != null)
                    {
                        _tcpClient.Close();
                        _tcpClient = null;
                    }

                    try
                    {
                        WriteToLogFile($"尝试连接 TCP 服务器：{TcpServerIp}:{TcpServerPort}");
                        _tcpClient = new TcpClient();
                        // 设置连接超时时间（例如 5 秒）
                        IAsyncResult result = _tcpClient.BeginConnect(TcpServerIp, TcpServerPort, null, null);

                        // 等待连接完成，如果超时则返回 false
                        bool success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5));
                        if (success)
                        {
                            _tcpClient.EndConnect(result);
                            WriteToLogFile("TCP 连接成功。");
                            return true;
                        }
                        else
                        {
                            _tcpClient.Close();
                            _tcpClient = null;
                            WriteToLogFile("TCP 连接超时。");
                            return false;
                        }
                    }
                    catch (Exception ex)
                    {
                        WriteToLogFile($"TCP 连接失败: {ex.Message}");
                        _tcpClient = null;
                        return false;
                    }
                }
                // 2. 否则，连接已打开
                return true;
            }
        }

        /// <summary>
        /// 发送数据到 TCP Server
        /// </summary>
        private void SendDataToTcpServer(string data)
        {
            if (!EnsureTcpConnection())
            {
                WriteToLogFile("TCP 发送失败：无法建立连接。");
                return;
            }

            try
            {
                NetworkStream stream = _tcpClient.GetStream();
                // 假设您的 Server 期望 UTF8 编码的数据
                byte[] buffer = Encoding.UTF8.GetBytes(data);

                // 写入数据
                stream.Write(buffer, 0, buffer.Length);

                // 关键：确保数据立即发送出去
                stream.Flush();

                WriteToLogFile($"TCP 发送成功：[{data}]");
            }
            catch (Exception ex)
            {
                WriteToLogFile($"TCP 发送过程中发生错误: {ex.Message}");

                // 发送失败后，断开连接，下次再尝试重连
                lock (_tcpLock)
                {
                    _tcpClient.Close();
                    _tcpClient = null;
                }
            }
        }
    }
}